package zjs.java;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Drawable;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;

class ResponseList<EventType> {
	// 链表节点
	private final class Node {
		Response<EventType> response;
		Node previous;
		Node next;
	}

	Node head;
	Node tail;

	// 移除器
	private final class ResponseRemover implements Remover {
		Node targetNode;

		public ResponseRemover( Node targetNode ) {
			this.targetNode = targetNode;
		}

		@Override
		public void remove() {
			if ( targetNode.previous != null ) {
				targetNode.previous.next = targetNode.next;
			}
			else {
				head = targetNode.next;
			}

			if ( targetNode.next != null ) {
				targetNode.next.previous = targetNode.previous;
			}
			else {
				tail = targetNode.previous;
			}
		}
	}

	// 注册响应,将响应加入到队列中
	public Remover regist( Response<EventType> response ) {
		final Node node = new Node();
		node.response = response;
		node.previous = this.tail;
		if ( this.head == null ) {
			this.head = node;
		}
		else {
			this.tail.next = node;
		}
		this.tail = node;

		return new ResponseRemover( node );
	}

	// 触发响应
	public void trig( EventType eventInfo ) {
		for ( Node curNode = head; curNode != null; curNode = curNode.next ) {
			curNode.response.trig( eventInfo );
		}
	}
}

/**
 * 区域系统
 * 
 * @author ZuoBai
 * 
 */
public class AreaSystem implements IArea {
	private Composite parentComposite;
	private Canvas canvas; // 根画布
	private Image imageBuffer; // 双重缓冲
	private Area[] hoverMatrix; // 悬停矩阵

	public class Area implements IArea {
		private Area parentArea; // 父亲区域
		private Area upperArea; // 置于其上的区域
		private Area lowerArea; // 置于其下的区域
		private Area topChildArea; // 顶层子元素
		private Area bottomChildArea; // 底层子元素

		private boolean isDisplay = true; // 显示位
		private Image canvasImage; // 画布图片
		private Image snapshot; // 快照

		private Area[] hoverMatrix; // 悬停矩阵

		private boolean isRegionDirty = false; // 范围脏位
		private boolean isSnapshotDitry = false; // 快照脏位
		private boolean isAreaDitry = false; // 区域脏位

		// 坐标
		private int x;
		private int y;
		private int absoluteX;
		private int absoluteY;

		// 大小和范围
		private int width;
		private int height;
		private byte[] regionMask; // 范围掩码

		private Area( Area parent ) {
			this.parentArea = parent;
		}

		// 设置区域脏位
		private void moveArea() {
			// 如果该区域触发了移动,意味着它的父区域的悬停矩阵和快照需要重新计算
			if ( this.parentArea != null && !this.parentArea.isAreaDitry ) {
				this.parentArea.isAreaDitry = true;
				this.parentArea.isSnapshotDitry = true;
				this.parentArea.moveArea();
			}
		}

		public int getX() {
			return x;
		}

		public void setX( int x ) {
			this.x = x;
			this.absoluteX = this.parentArea.absoluteX + x;
			moveArea();
		}

		public int getY() {
			return y;
		}

		public void setY( int y ) {
			this.y = y;
			this.absoluteY = this.parentArea.absoluteY + y;
			moveArea();
		}

		public void visible( boolean flag ) {
			this.isDisplay = flag;
			moveArea();
		}

		/**
		 * 将该区域插入到目标区域之上,注意两个区域必须处于同一层,若目标区域是null,则置底
		 * 
		 * @param targetArea
		 *            目标区域,若为null,则置底
		 */
		public void insertUpper( Area targetArea ) {
			AreaSystem.insertUpper( this, targetArea );
			moveArea();
		}

		/**
		 * 获取该区域的最顶层兄弟区域
		 */
		public Area getTopArea() {
			return this.parentArea.topChildArea;
		}

		/**
		 * 获取该区域的最底层兄弟区域
		 */
		public Area getBottomArea() {
			return this.parentArea.bottomChildArea;
		}

		private void resizeArea() {
			// 如果一个区域的尺寸范围改变,意味着该区域的区域矩阵和快照需要重新计算
			if ( !this.isAreaDitry ) {
				this.isAreaDitry = true;
				this.isSnapshotDitry = true;
				if ( this.parentArea != null ) {
					this.parentArea.resizeArea();
				}
			}
		}

		/**
		 * 重新设定区域尺寸和范围
		 * 
		 * @param width
		 *            宽
		 * @param height
		 *            高
		 * @param region
		 *            范围,多边形节点
		 */
		public void resize( int width, int height, byte[] region ) {
			assert width * height == region.length;
			this.width = width;
			this.height = height;
			this.regionMask = region;
			this.isRegionDirty = true;
			resizeArea();
		}

		/**
		 * 分配一个区域
		 */
		public Area area() {
			Area area = new Area( this );
			area.lowerArea = this.topChildArea;
			if ( this.topChildArea == null ) {
				this.bottomChildArea = area;
			}
			else {
				this.topChildArea.upperArea = area;
			}
			this.topChildArea = area;
			return area;
		}

		// 设置画笔脏位
		private void setCanvasDirty() {
			if ( !this.isSnapshotDitry ) {
				this.isSnapshotDitry = true;
				if ( this.parentArea != null ) {
					this.parentArea.setCanvasDirty();
				}
			}
		}

		public class Brush {
			GC gc;

			Brush( Drawable drawable ) {
				gc = new GC( drawable );
			}

			public void setAlpha( int alpha ) {
				gc.setAlpha( alpha );
			}

			public void setBackground( Color color ) {
				gc.setBackground( color );
			}

			public void fillRectangle( int x, int y, int width, int height ) {
				setCanvasDirty();
				gc.fillRectangle( x, y, width, height );
			}
		}

		/**
		 * 设置一块新的画布,返回该画布的画笔
		 * 
		 * @param width
		 *            宽
		 * @param height
		 *            高
		 * @return 该画布的画笔
		 */
		public Brush newCanvas( int width, int height ) {
			if ( canvasImage != null ) {
				canvasImage.dispose();
			}

			canvasImage = new Image( parentComposite.getDisplay(), width, height );
			return new Brush( canvasImage );
		}

		ResponseList<CursorChangeEvent> cursorIn = new ResponseList<CursorChangeEvent>();
		ResponseList<CursorChangeEvent> cursorOut = new ResponseList<CursorChangeEvent>();
		ResponseList<CursorMoveEvent> cursorMove = new ResponseList<CursorMoveEvent>();
		ResponseList<CursorClickEvent> cursorDown = new ResponseList<CursorClickEvent>();
		ResponseList<CursorClickEvent> cursorUp = new ResponseList<CursorClickEvent>();

		public Remover onCursorIn( Response<CursorChangeEvent> response ) {
			return this.cursorIn.regist( response );
		}

		public Remover onCursorOut( Response<CursorChangeEvent> response ) {
			return this.cursorOut.regist( response );
		}

		public Remover onCursorMove( Response<CursorMoveEvent> response ) {
			return this.cursorMove.regist( response );
		}

		public Remover onCursorUp( Response<CursorClickEvent> response ) {
			return this.cursorUp.regist( response );
		}

		public Remover onCursorDown( Response<CursorClickEvent> response ) {
			return this.cursorDown.regist( response );
		}
	}

	// 将区域从链表中抽出
	private static void takeAreaOutOfLinkedList( Area targetArea ) {
		// 如果该区域有上层区域,设置上层区域的下层区域为该区域的下层区域
		if ( targetArea.upperArea != null ) {
			targetArea.upperArea.lowerArea = targetArea.lowerArea;
		}
		// 否则该区域是顶层区域
		else {
			targetArea.parentArea.topChildArea = targetArea.lowerArea;
		}

		if ( targetArea.lowerArea != null ) {
			targetArea.lowerArea.upperArea = targetArea.upperArea;
		}
		else {
			targetArea.parentArea.bottomChildArea = targetArea.upperArea;
		}

	}

	// 将源区域插入到目标区域之上,如果targetArea是null,则置于最底层
	private static void insertUpper( Area sourceArea, Area targetArea ) {
		// 自己插自己,直接放回,不做任何事情
		if ( sourceArea == targetArea ) {
			return;
		}

		Area parentArea = sourceArea.parentArea;

		// 将源节点从链表中取出
		takeAreaOutOfLinkedList( sourceArea );

		// 如果目标区域是null,置于最底层
		if ( targetArea == null ) {
			if ( parentArea.topChildArea == null ) {
				parentArea.topChildArea = sourceArea;
			}
			else {
				parentArea.bottomChildArea.lowerArea = sourceArea;
			}
			sourceArea.upperArea = sourceArea.parentArea.bottomChildArea;
			parentArea.bottomChildArea = sourceArea;
			return;
		}

		// 两者必须是兄弟节点
		assert ( parentArea == targetArea.parentArea );

		if ( targetArea.upperArea != null ) {
			targetArea.upperArea.lowerArea = sourceArea;
		}
		else {
			parentArea.topChildArea = sourceArea;
		}
		sourceArea.upperArea = targetArea.upperArea;
		targetArea.upperArea = sourceArea;
		sourceArea.lowerArea = targetArea;
	}

	private Area rootArea; // 根区域

	private int x = 0;
	private int y = 0;

	// 光标位置
	private int cursorX;
	private int cursorY;
	private Area hoverArea = null; // 悬停区域

	private static class AreaNode {
		Area area;
		AreaNode next;
	}

	static AreaNode getParentTree( Area area ) {
		if ( area == null ) {
			return null;
		}
		else {
			AreaNode node = new AreaNode();
			node.area = area;
			node.next = getSonTree( area.parentArea );
			return node;
		}
	}

	static AreaNode getSonTree( Area area ) {
		AreaNode parentTree = getParentTree( area );
		AreaNode retVal = null;

		while ( parentTree != null ) {
			AreaNode node = new AreaNode();
			node.next = retVal;
			node.area = parentTree.area;
			retVal = node;
			parentTree = parentTree.next;
		}

		return retVal;
	}

	enum MouseChange {
		IN, OUT
	}

	private void trigMouseChange( Area area, MouseEvent e, MouseChange type ) {
		CursorChangeEvent ce = new CursorChangeEvent( e, e.x - area.absoluteX, e.y - area.absoluteY );
		if ( type == MouseChange.IN ) {
			area.cursorIn.trig( ce );
		}
		else {
			area.cursorOut.trig( ce );
		}
	}

	private void cursorMove( MouseEvent e, Area newArea, boolean trigMove ) {
		// TODO 添加注释
		AreaNode srcTree = getSonTree( this.hoverArea );
		AreaNode tarTree = getSonTree( newArea );

		AreaNode curSrcNode = srcTree, curTarNode = tarTree;
		while ( curSrcNode != null || curTarNode != null ) {
			if ( curSrcNode == null ) {
				trigMouseChange( curTarNode.area, e, MouseChange.IN );
				curTarNode = curTarNode.next;
			}
			else if ( curTarNode == null ) {
				trigMouseChange( curSrcNode.area, e, MouseChange.OUT );
				curSrcNode = curSrcNode.next;
			}
			else {
				if ( curSrcNode.area != curTarNode.area ) {
					trigMouseChange( curTarNode.area, e, MouseChange.IN );
					trigMouseChange( curSrcNode.area, e, MouseChange.OUT );
				}
				else if ( trigMove ) {
					Area area = curSrcNode.area;
					area.cursorMove.trig( new CursorMoveEvent( e, this.cursorX - area.absoluteX, this.cursorY - area.absoluteY, e.x - area.absoluteX, e.y
							- area.absoluteY ) );
				}
				curTarNode = curTarNode.next;
				curSrcNode = curSrcNode.next;
			}

			this.cursorX = x;
			this.cursorY = y;
			this.hoverArea = newArea;
		}
	}

	private Area getHoverArea( int x, int y ) {
		if ( x >= 0 && x < rootArea.width && y >= 0 && y < rootArea.height ) {
			return rootArea.hoverMatrix[y * rootArea.width + x];
		}
		else {
			return null;
		}
	}

	private AreaSystem( Composite parent ) {
		this.parentComposite = parent;
		this.canvas = new Canvas( parent, SWT.DOUBLE_BUFFERED );
		this.rootArea = new Area( null );

		// 重绘事件,使用双重缓冲实现
		this.canvas.addPaintListener( new PaintListener() {
			@Override
			public void paintControl( PaintEvent e ) {
				e.gc.drawImage( rootArea.snapshot, 0, 0 );
			}
		} );

		// 5个鼠标事件的实现
		this.canvas.addMouseListener( new MouseListener() {
			@Override
			public void mouseUp( MouseEvent e ) {
				Area hoverArea = getHoverArea( e.x, e.y );
				for ( Area curArea = hoverArea; curArea != null; curArea = curArea.parentArea ) {
					CursorClickEvent event = new CursorClickEvent( e, e.x - curArea.absoluteX, e.y - curArea.absoluteY );
					curArea.cursorUp.trig( event );
				}
			}

			@Override
			public void mouseDown( MouseEvent e ) {
				Area hoverArea = getHoverArea( e.x, e.y );
				for ( Area curArea = hoverArea; curArea != null; curArea = curArea.parentArea ) {
					CursorClickEvent event = new CursorClickEvent( e, e.x - curArea.absoluteX, e.y - curArea.absoluteY );
					curArea.cursorDown.trig( event );
				}
			}

			@Override
			public void mouseDoubleClick( MouseEvent e ) {}
		} );

		this.canvas.addMouseMoveListener( new MouseMoveListener() {
			@Override
			public void mouseMove( MouseEvent e ) {
				if ( e.x >= 0 && e.x < rootArea.width && e.y >= 0 && e.y < rootArea.height ) {
					cursorMove( e, getHoverArea( e.x, e.y ), true );
				}
			}
		} );

		this.canvas.addMouseTrackListener( new MouseTrackListener() {
			@Override
			public void mouseExit( MouseEvent e ) {
				cursorMove( e, null, false );
			}

			@Override
			public void mouseEnter( MouseEvent e ) {
				cursorMove( e, getHoverArea( e.x, e.y ), false );
			}

			@Override
			public void mouseHover( MouseEvent e ) {}
		} );
	}

	public void setX( int x ) {
		this.x = x;
		canvas.setBounds( x, this.y, rootArea.width, rootArea.height );
	}

	public void setY( int y ) {
		this.y = y;
		canvas.setBounds( this.x, y, rootArea.width, rootArea.height );
	}

	// 计算区域和悬停矩阵
	private void caculateArea( Area area ) {
		int width = area.width, height = area.height;

		// 如果范围是脏的,重新分配区域矩阵和快照
		if ( area.isRegionDirty ) {
			area.hoverMatrix = new Area[width * height];

			// 如果原来有快照,析构它
			if ( area.snapshot != null ) {
				area.snapshot.dispose();
			}

			// 换个新快照和画笔
			area.snapshot = new Image( parentComposite.getDisplay(), area.width, area.height );

			area.isRegionDirty = false;
		}

		Area[] parentMatrix = area.hoverMatrix;

		// 填充底
		for ( int i = 0, len = parentMatrix.length; i != len; ++i ) {
			parentMatrix[i] = area;
		}

		// 填充子区域
		for ( Area curArea = area.bottomChildArea; curArea != null; curArea = curArea.upperArea ) {
			// 如果该区域是脏的,递归处理该区域
			if ( curArea.isAreaDitry ) {
				caculateArea( curArea );
			}
			Area[] curMatrix = curArea.hoverMatrix;
			int curWidth = curArea.width, curHeight = curArea.height, curRight = curArea.x + curWidth, curBottom = curArea.y + curHeight;

			// 如果子区域在父区域范围内,填充父区域的悬停矩阵
			if ( curArea.x < width && curRight > 0 && curArea.y < height && curBottom > 0 ) {
				int inY = curArea.y < 0 ? 0 - curArea.y : 0, outY = inY + curArea.y, inBottom = height >= curBottom ? curHeight : height - curArea.y;
				for ( ; inY != inBottom; ++inY, ++outY ) {
					int inX = ( curArea.x < 0 ? 0 - curArea.x : 0 ), inPos = inY * curWidth + inX, outPos = outY * width + inX + curArea.x;
					int inRight = inY * curWidth + ( width >= curRight ? curWidth : width - curArea.x );
					for ( ; inPos != inRight; ++inPos, ++outPos ) {
						if ( curMatrix[inPos] != null ) {
							parentMatrix[outPos] = curMatrix[inPos];
						}
					}
				}
			}
		}

		// 如果设定了范围,根据掩码进行剪裁
		if ( area.regionMask != null ) {
			byte[] regionMask = area.regionMask;
			for ( int i = 0, len = regionMask.length; i != len; ++i ) {
				if ( regionMask[i] == 0 ) {
					parentMatrix[i] = null;
				}
			}
		}

		area.isAreaDitry = false;
	}

	// 绘制区域,填充悬停矩阵
	private void drawArea( Area area ) {
		GC gc = new GC( area.snapshot );

		// 如果有画布,将画布画在snapshot上
		if ( area.canvasImage != null ) {
			gc.drawImage( area.canvasImage, 0, 0 );
		}

		// 将子区域的snapshot画在该区域的snapshot上
		for ( Area curArea = area.bottomChildArea; curArea != null; curArea = curArea.upperArea ) {
			// 如果子区域的快照是脏的,重绘子区域
			if ( curArea.isSnapshotDitry ) {
				drawArea( curArea );
			}

			if ( curArea.isDisplay ) {
				gc.drawImage( curArea.snapshot, curArea.x, curArea.y );
			}
		}

		// 如果有范围,根据范围进行截取
		if ( area.regionMask != null ) {
			ImageData imageData = area.snapshot.getImageData();
			imageData.alphaData = area.regionMask;
			area.snapshot.dispose();
			area.snapshot = new Image( parentComposite.getDisplay(), imageData );
		}

		gc.dispose();

		area.isSnapshotDitry = false;
	}

	/**
	 * 重新设定区域尺寸和范围
	 * 
	 * @param width
	 *            宽
	 * @param height
	 *            高
	 * @param regionMask
	 *            范围,多边形节点
	 */
	public void redraw() {
		// 如果区域脏了,计算区域
		if ( this.rootArea.isAreaDitry ) {
			caculateArea( this.rootArea );
		}

		// 如果快照脏了,计算画布,重绘
		if ( this.rootArea.isSnapshotDitry ) {
			drawArea( this.rootArea );

			canvas.redraw();
		}
	}

	public void resize( int width, int height, byte[] region ) {
		canvas.setBounds( this.x, this.y, width, height );
		rootArea.resize( width, height, region );
	}

	/**
	 * 设置一块新的画布,返回该画布的画笔
	 * 
	 * @param width
	 *            宽
	 * @param height
	 *            高
	 * @return 该画布的画笔
	 */
	public Area.Brush newCanvas( int width, int height ) {
		return this.rootArea.newCanvas( width, height );
	}

	/**
	 * 分配一个新区域
	 */
	public Area area() {
		return rootArea.area();
	}

	public Remover onCursorIn( Response<CursorChangeEvent> response ) {
		return this.rootArea.onCursorIn( response );
	}

	public Remover onCursorOut( Response<CursorChangeEvent> response ) {
		return this.rootArea.onCursorOut( response );
	}

	public Remover onCursorMove( Response<CursorMoveEvent> response ) {
		return this.rootArea.onCursorMove( response );
	}

	public Remover onCursorUp( Response<CursorClickEvent> response ) {
		return this.rootArea.onCursorUp( response );
	}

	public Remover onCursorDown( Response<CursorClickEvent> response ) {
		return this.rootArea.onCursorDown( response );
	}

	static public AreaSystem newAreaSystem( Composite parent ) {
		return new AreaSystem( parent );
	}
}
